<?php

1->远程debug
参考: http://www.codebaoku.com/it-java/it-java-221274.html
. nohup java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:42917 -jar aiot-admin-0.8.0.jar > admin.log &
. Edit Configuration -> 新建 -> Remote JVM Debug
   配置:
   . Debugger mode: Attach to remote JVM
   . Host: 代码部署的服务器ip / Port: debug制定的端口



2->ReflectASM
参考: https://www.bbsmax.com/A/n2d9bg60zD/
使用:
[pom]
<!-- ReflectASM -->
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>reflectasm</artifactId>
    <version>1.11.9</version>
</dependency>

[代码]
详见: 附录/reflectasms/

// Get MethodAccess From Cache
MethodAccess methodAccess = ReflectAsmManager.get(obj.getClass());
// get
Object value = methodAccess.invoke(obj, "get" + StringUtils.capitalize(field.getName()));
// set 根据字段类型设置字段值
methodAccess.invoke(obj, "set" + StringUtils.capitalize(field.getName()), Integer.parseInt(mapValue.toString()));

[示例]
/**
 * 校验对象字段是否包含某个值, 包含的话就返回字段名称
 */
public static String checkObjContainsValue(Object targetObj, String checkField, Object targetValue, boolean isSet) {
    Class<T> aClass = targetObj.getClass();
    Field[] fatherField = aClass.getSuperclass().getDeclaredFields();
    Field[] childrenField = aClass.getDeclaredFields();
    List<Field> fieldsList = new ArrayList(fatherField.length + childrenField.length);
    fieldsList.addAll(Arrays.asList(fatherField));
    fieldsList.addAll(Arrays.asList(childrenField));
    List<Field> fields = fieldsList;
    if (StringUtils.isNotEmpty(checkField)) {
        fields = fieldsList.stream().filter(item -> item.getName().startsWith(checkField)).collect(Collectors.toList());
    }
    // 设置返回的字段
    String fieldName = "";
    // 移除非状态字段
    List<String> removeList = Arrays.asList(REMOVE_FIELD);
    fields.removeIf(item -> removeList.contains(item.getName().toLowerCase()));
    for (Field field : fields) {
        MethodAccess methodAccess = ReflectAsmManager.get(aClass);
        if (isSet) {
            methodAccess.invoke(targetObj, "set" + StringUtils.capitalize(field.getName()), targetValue);
        } else {
            Object value = methodAccess.invoke(targetObj, "get" + StringUtils.capitalize(field.getName()));
            if (value.equals(targetValue)) {
                fieldName = field.getName();
            }
        }
    }
    return fieldName;
}

注意:
- [get/set]后面获取的字段首字母需要大写!
- 每次获取时会比较慢, 建议获取后放入缓存



3->RXJava
参考:
https://github.com/ReactiveX/RxJava
https://blog.csdn.net/LucasXu01/article/details/105279367
说明:
我们不可能每次点击事件之后都去上报服务器，那样的话会导致其他网络请求变慢。
两种解决方案的
. 一个一个就是满了多少条之后就同意上报。
. 另外就是需要把一个时间段的点击事件统一上报。

使用:
[pom]
<dependencies>
    <dependency>
        <groupId>io.reactivex.rxjava2</groupId>
        <artifactId>rxjava</artifactId>
        <version>2.2.19</version>
    </dependency>
</dependencies>

[代码]
详情: 附录/rxjava/



4->Spring的事件传播
说明: Spring支持类继承事件, 然后在类发生变化的时候, 对其进行监听
流程:
. [step1]: 定义事件实体类 (用于发送和接收的业务处理对象)
@Data
@Builder
public class XmTestInfo {
    private final String userName;
    private final Integer age;

    public XmTestInfo(String userName, Integer age) {
        this.userName = userName;
        this.age = age;
    }
}

. [step2]: 定义事件 (当该类被调用时, 那么监听该类的类, 就会执行 - '生产者')
@Getter
public class XmTestEvent extends ApplicationEvent {

    private final transient XmTestInfo xmTestInfo;

    public XmTestEvent(Object source, XmTestInfo xmTestInfo) {
        super(source);
        this.xmTestInfo = xmTestInfo;
    }
}

. [step3]: 定义事件监听类 (监听事件类被调用 - '消费者')
@Slf4j
@Component
public class XmTestListenser implements ApplicationListener<XmTestEvent> {

    XmTestListenser() {
    }

    @Override
    public void onApplicationEvent(XmTestEvent xmTestEvent) {
        final XmTestInfo xmTestInfo = xmTestEvent.getXmTestInfo();
        log.info(JSON.toJSONString(xmTestInfo));
    }
}

. [step4]: 使用
@GetMapping("/sendEvent")
public void sendEvent() {
    final XmTestInfo xmTestInfo = XmTestInfo.builder().userName("小木").age(18).build();
    SpringUtils.publishEvent(new XmTestEvent(this, xmTestInfo));
}
SpringUtils: 附录/spring/SpringUtil.java



5->慢启动检测
@Component
public class BeanInitCostTimeBeanPostProcessor implements BeanPostProcessor {

    private static final Logger logger = LoggerFactory.getLogger(BeanInitCostTimeBeanPostProcessor.class);

    private static final ConcurrentHashMap<String, Long> START_TIME = new ConcurrentHashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        START_TIME.put(beanName, System.currentTimeMillis());
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
     if(Objects.nonNull(START_TIME.get(beanName))){
         long costTime = System.currentTimeMillis() - START_TIME.get(beanName);
          if(costTime>300) {
              logger.error("beanName:{},cost:{}",beanName,costTime);
          }
     }
        START_TIME.clear();
        return bean;
    }
}



6->WebSocket
详见: 附录/websocket
- ServerEncode.java
- WebSocketServer.java
[使用]
/**
 * 5秒推送一次[fms]日志
 */
@Scheduled(fixedDelay = 5000)
public void pushInteractiveFmsLog() {
    SearchWebSocketLogBo socketLogBo = WebSocketTracker.getByKey(WebSocketTracker.WMS_KEY);
    socketLogBo.setTenantId(PreTenantContextHolder.getCurrentTenantId());
    Object responseBody = httpRestUtil.sendJsonPost(dataWayWcsUrl, null, JSONObject.toJSONString(socketLogBo));
    boolean allowPush = wmsLogScheduleTask.checkLogAllowPush(responseBody);
    if (allowPush) {
        WebSocketServer.sendInfoBySid(responseBody, "wsPushInteractiveFmsLog");
    }
}
说明: 虽然有'转码类'但是也会是不是推送数据为[Object]格式, 所以前端需要做个判断






















.
